#include	"resource.h"
#include	"miniz.h"
#include	<memory>
#include	<cstring>

#if defined(_WIN32)
#include <Windows.h>
static char * res_start = nullptr;
static size_t res_size = 0;
#else
extern char _binary_res_zip_start;
extern char _binary_res_zip_end;
#endif

static std::string MimeType(const std::string & sName) {
	auto nIdx = sName.find_last_of(".");
	if (nIdx == std::string::npos || nIdx == sName.size() - 1) return "text/plain";

	auto sExt = sName.substr(nIdx + 1, sName.size() - nIdx - 1);
	if (sExt == "html" || sExt == "htm" || sExt == "shtm" || sExt == "shtml") {
		return "text/html";
	} else if (sExt == "css") {
		return "text/css";
	} else if (sExt == "js") {
		return "application/x-javascript";
	} else if (sExt == "ico" || sExt == "icon") {
		return "image/x-icon";
	} else if (sExt == "gif") {
		return "image/gif";
	} else if (sExt == "jpg" || sExt == "jpeg") {
		return "image/jpeg";
	} else if (sExt == "png") {
		return "image/png";
	} else if (sExt == "svg") {
		return "image/svg+xml";
	} else if (sExt == "txt") {
		return "text/plain";
	} else if (sExt == "xml" || sExt == "xslt" || sExt == "xsl") {
		return "text/xml";
	} else if (sExt == "ttf") {
		return "application/x-font-ttf";
	} else {
		return "text/plain";
	}
}

Resource::~Resource() {
	for (auto & kv : _mAssets) {
		free(kv.second.pData);
	}

	if (_pZip) {
		mz_zip_reader_end(_pZip);
		delete _pZip;
	}

	_mAssets.clear();
}

Resource & Resource::Instance() {
	static std::unique_ptr<Resource> GIns;
	if (!GIns) GIns.reset(new Resource);
	return *GIns;
}

bool Resource::Load() {
#if defined(_WIN32)
	HRSRC hR = ::FindResourceA(NULL, "IDR_RES_ZIP", RT_RCDATA);
	if (!hR) return false;
	
	res_size = ::SizeofResource(NULL, hR);
	res_start = new char[res_size];
	if (!res_start) return false;

	HGLOBAL hG = (LPBYTE)::LoadResource(NULL, hR);
	if (!hG) return false;
	
	LPBYTE lpData = (LPBYTE)::LockResource(hG);
	memcpy(res_start, lpData, res_size);
	::FreeResource(hG);

	void * pData = res_start;
	size_t nSize = res_size;
#else
	void * pData = &_binary_res_zip_start;
	size_t nSize = (&_binary_res_zip_end - &_binary_res_zip_start);
#endif

	_pZip = new mz_zip_archive_tag;
	memset(_pZip, 0, sizeof(mz_zip_archive_tag));

	if (!mz_zip_reader_init_mem(_pZip, pData, nSize, 0)) return false;

	mz_uint nCount = mz_zip_reader_get_num_files(_pZip);
	for (mz_uint nIdx = 0; nIdx < nCount; ++nIdx) {
		mz_zip_archive_file_stat iStat;
		if (mz_zip_reader_is_file_a_directory(_pZip, nIdx) || !mz_zip_reader_file_stat(_pZip, nIdx, &iStat)) continue;
		
		std::string sType = MimeType(iStat.m_filename);
		Asset iAsset;
		memset(&iAsset, 0, sizeof(iAsset));

		iAsset.pData = (char *)mz_zip_reader_extract_to_heap(_pZip, nIdx, &iAsset.nSize, 0);
		strncpy_s(iAsset.pType, sType.data(), sType.size());

		_mAssets[iStat.m_filename] = iAsset;
	}
	
	return true;
}

Asset * Resource::Get(const std::string & sPath) {
	auto it = _mAssets.find(sPath);
	if (it == _mAssets.end()) return nullptr;
	return &it->second;
}
